#!/usr/bin/python3
###############################################################################
'''3阶Sallen-Key滤波器参数求解'''
# Copyright (c) 2022 Xu Ruijun | 1687701765@qq.com
#
# This file is part of Electronic Analog Filter Design Tool(eAFDTool).
#
# eAFDTool is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or any later version.
#
# eAFDTool is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# eAFDTool. If not, see <https://www.gnu.org/licenses/>.
###############################################################################
from .basic_solver import RC_filter
from ..equation import solve_x2

__author__ = "Xu Ruijun"
__copyright__ = "Copyright (c) 2022 Xu Ruijun"
__license__ = "GPLv3 or later"


class SK_LPF3(RC_filter):
    rx   = [0, 1, 2]
    cx   = [3, 4, 5]
    rcn  = ['r1', 'r2', 'r3', 'c1', 'c2', 'c3']
    t2rc = [[0, 3], [1, 4], [2, 5], [0, 4], [0, 5], [1, 5]]
    tname= ['R1C1', 'R2C2', 'R3C3', 'R1C2', 'R1C3', 'R2C3']
    tchr = ['x',    'y',    'z',    'u',    'v',    'w'   ]
    def t_adj(self, **kwargs):
        """
        这个解方程程序不是很完善，目的是代替sympy来解方程和迭代求解，提高速度
        产生多个解时只能返回其中一个
        建议在{x,y,z}中选两个变量，或x,v组合
        """
        a, b, c = self.A[:3]
        ivars = self.process_relat_vars(kwargs, 'xyzuvw')
        assert ivars.count(None) == 4  # 四个未知变量
        x, y, z, u, v, w = ivars
        for i in range(10):
            # x * y * z = a
            vl = [x, y, z]
            if vl.count(None) == 1:
                i = vl.index(None)
                if i == 0:
                    x = a/(y*z)
                elif i == 1:
                    y = a/(x*z)
                elif i == 2:
                    z = a/(x*y)
            # x + v + w + z = c
            vl = [x, v, w, z]
            if vl.count(None) == 1:
                i = vl.index(None)
                vl.pop(i)
                t = c - sum(vl)
                if i == 0:
                    x = t
                elif i == 1:
                    v = t
                elif i == 2:
                    w = t
                elif i == 3:
                    z = t
            # u*w = y*v
            vl = [u, w, y, v]
            if vl.count(None) == 1:
                i = vl.index(None)
                if i == 0:
                    u = y*v/w
                elif i == 1:
                    w = y*v/u
                elif i == 2:
                    y = u*w/v
                elif i == 3:
                    v = u*w/y
            # x*v + x*z + u*z + y*z = b
            vl = [x, y, z, u, v]
            if vl.count(None) == 1:
                i = vl.index(None)
                if i < 3:
                    pass
                elif i == 3:
                    u = (b - x*v - x*z - y*z)/z
                elif i == 4:
                    v = (b - x*z - u*z - y*z)/x
            # x, v -> z
            if [x, v].count(None) == 0 and z is None:
                b_ = b - a/x - x*v
                c_ = c - x - v
                z = b_*x / (c_*x**2 + a*v)
            # x, y, z -> v
            if [x, y, z].count(None) == 0 and v is None:
                b_ = b - x*z - y*z
                c_ = c - x - z
                X1, X2 = solve_x2(x, -(b_ + c_*x + y*z), b_*c_)
                #print(f'X1={X1}, X2={X2}')
                v = X2  # 取v=X1无法搜索到正实数解
            # finally
            lst = [x, y, z, u, v, w]
            if lst.count(None) == 0:
                break
        else:
            raise RuntimeError('找不到解')

        # 检查符合要求
        for o, n in zip(ivars, lst):
            if o is not None and o != n:
                raise ValueError(f'{lold} -> {lst}')
        # 检查符合系统
        self.verify(*lst)
        self.t = lst
        return lst

    def verify(self, x, y, z, u, v, w):
        a, b, c = self.A[:3]
        ra = x*y*z
        rb = x*v + x*z + u*z + y*z
        rc = x + v + w + z
        rd = (u*w) / (y*v)
        assert abs(ra / a - 1) <= 1e-6, f'{ra:f} != {a:f}'
        assert abs(rb / b - 1) <= 1e-6, f'{rb:f} != {b:f}'
        assert abs(rc / c - 1) <= 1e-6, f'{rc:f} != {c:f}'
        assert abs(rd - 1) <= 1e-6, f'{rd-1}'

    def initv(self):
        # 以下是结果全为正数的一个经验值，仅供参考
        return self.t_adj(x_=1.1, y_=1.7)
